<script setup lang="ts">
// Utils
import { dom, useQuasar } from '@teld/q-components';
import useMappingProps from 't/common/hooks/useMappingProps';
import { commonProps } from 't/common/utils';
import {
  computed,
  defineExpose,
  defineProps,
  nextTick,
  ref,
  onMounted,
  watch,
} from 'vue';
import { PAGER_STYLES, PAGING_TYPE } from './constants.js';
import myProps from './props.js';
import _ from 'lodash';
import NoData from './no-data.vue';
import useScrollNotify from './hooks/useScrollNotify.js';
import ScrollOutside from './scroll-outside/scroll-outside.vue';

// Components
defineOptions({
  name: 'TRepeat',
});

import { PullRefresh as VPullToRefresh } from 'vant';
import 'vant/es/pull-refresh/style';

const $q = useQuasar();
const $emit = defineEmits([
  'scroll-load',
  'scroll-loaded',
  'pull-refresh',
  'update:list',
  'page-load',
]);

const props = defineProps({
  ...commonProps,
  ...myProps,
});

/**
 * TList无法控制关闭自动加载，只能通过 v-if 不加载
 * 这里用到了本地变量 autoLoad 来控制挂载 TList
 */
const reloadList = () => {
  if (!autoLoad.value) {
    // 纯首次手动加载：默认不自动加载，通过 reload 方法实现的首次加载
    autoLoad.value = true;
    return;
  }

  // 清空数据
  $emit('update:list', []);

  if (props.showIndexAndCount) {
    // 避免reload后弹出提示不对
    stopObserve();
  }

  if (isFinished.value) {
    isReloading.value = true;
    nextTick(() => (isReloading.value = false));
  } else {
    nextTick(() => {
      if (scrollRef.value) {
        scrollRef.value.reset();
        scrollRef.value.trigger();
      }
    });
  }
  isFinished.value = false;
};

/**
 * TTable 自身没有自动加载，需要手动加载第一页数据
 * 重新加载时也是一样，手动加载第一页即可
 */
const reloadTable = () => {
  tablePaging.value.page = 1;
  onRequest({ pagination: tablePaging.value });
};

/**重新获取数据*/
const reload = () => {debugger
  // props.pagingType === PAGING_TYPE.Pagination ? reloadTable() : reloadList();
  if (props.pagingType === PAGING_TYPE.Pagination) {
    reloadTable();
  } else if (props.isScrollOutside) {
    $emit('update:list', []);
    scrollOutsideRef.value.reload();
  } else {
    reloadList();
  }
};

const isFinished = ref(false);
const isReloading = ref(false);
const listRef = ref();
const scrollRef = ref();
const listTotal = ref(0); // 用于TList滚动加载时提示当前位置
let refreshDone = null;
const toExpose = {
  reload,
  list: computed(() => props.list),
  resize: () => {
    const _fillMode_ = mappingProps.fillMode;
    mappingProps.fillMode = '_';
    nextTick(() => {
      mappingProps.fillMode = _fillMode_;
    });
  },
};
const [mappingProps, mappingExpose] = useMappingProps(props, toExpose);
const autoLoad = ref(mappingProps.autoLoad);

defineExpose(mappingExpose);

/**
 * 滚动加载模式下，计算列表高度
 * 注意：必须有高度才能出现滚动条这是滚动加载的前提
 * fillMode: none|fill|fixed
 * 滚动加载模式下，none即fill
 */
const computedHeight = computed(() => {
  const top = listRef.value?.$el ? dom.offset(listRef.value?.$el).top : 0;
  const restHeight = $q.screen.height - top;

  let bottomGap = 0;
  try {
    bottomGap = parseInt(mappingProps.fillMarginBottom);
  } catch (error) {
    console.log(error);
  }

  const isAuto =
    mappingProps.fillMode === 'none' ||
    mappingProps.fillMode === 'fill' ||
    ['auto', 'fill'].includes((mappingProps.height || '').trim().toLowerCase());
  const hValue = restHeight <= 0 ? mappingProps.height || undefined : restHeight;
  const h = isAuto
    ? `calc(${hValue}px - ${bottomGap || 0}px)`
    : props.height || undefined;

  return h;
});

//#region 滚动时动态提示当前元素index及数据总数
const {
  scrollCounting,
  scrollCountingVisible,
  beginObserveAfterScrollLoaded,
  resetObserver,
  stopObserve,
} = useScrollNotify(listRef, listTotal, mappingProps);

const scrollContainerVisible = computed(
  () => autoLoad.value && !isReloading.value,
);

//#endregion

const onScrollLoad = (index, done) => {
  $emit('scroll-load', index, props.pageSize, ({ stop, totalCount }) => {
    isFinished.value = stop;
    listTotal.value = totalCount;
    done(stop);
    $emit('scroll-loaded', {
      totalCount: totalCount,
      pageIndex: index,
      pageSize: props.pageSize,
    });

    if (props.isScrollOutside) {
      return;
    }

    if (refreshDone) {
      // @ts-ignore
      refreshDone();
      refreshDone = null;
    }

    if (props.showIndexAndCount && totalCount) {
      // 加载完成后，给列表元素添加监听，用于滚动时提示当前位置
      nextTick(() => {
        // 默认认为循环项等高，在加载第一页数据时，根据循环项高度动态设置监测条件
        if (index === 1) {
          // 一页多个时，循环项完全展示时点处理，显示不开单个时，滚动离开的时点处理
          const itemHeight = scrollRef.value.$el.children[0].clientHeight;
          resetObserver(itemHeight);
        }

        beginObserveAfterScrollLoaded(scrollRef.value.$el.children);
      });
    } else {
      scrollCountingVisible.value = false;
      stopObserve();
    }
  });
};

const scrollOutsideRef = ref(null);
const onOuterScrollLoad = (index, callback) => {
  if (!mappingProps.isScrollOutside) {
    return;
  }
  onScrollLoad(index, callback);
}

const isRefreshing = ref(false);
const onPullRefresh = done => {
  $emit('pull-refresh');
  // refreshDone = done;
  // @ts-ignore
  refreshDone = () => {
    isRefreshing.value = false; // 通知Vant刷新完成
  }
  reloadList();
};

// #region Table and Pagination Mode

const tableRef = ref();

/**
 * 分页器模式下，计算表格高度
 * 不同于滚动加载，分页时表格高度不是必须要设的
 */
const computedTableHeight = computed(() => {
  const top = tableRef.value?.$el ? dom.offset(tableRef.value?.$el).top : 0;
  const restHeight = $q.screen.height - top;

  let bottomGap = 0;
  let propsHeight = 0;
  try {
    bottomGap = parseInt(mappingProps.fillMarginBottom);
    propsHeight = parseInt(mappingProps.height);
  } catch (error) {
    console.log(error);
  }

  const isAuto =
    props.fillMode === 'none' ||
    (props.fillMode === 'fixed' &&
      (props.height || '').trim().toLowerCase() === 'auto');
  const isFill =
    props.fillMode === 'fill' ||
    (props.fillMode === 'fixed' &&
      (props.height || '').trim().toLowerCase() === 'fill');

  const hValue = restHeight <= 0 ? propsHeight || undefined : restHeight;

  const h =
    isFill && hValue
      ? `calc(${hValue}px - ${bottomGap || 0}px)`
      : isAuto
        ? 'auto'
        : mappingProps.height || undefined;

  return h;
});
// Table 分页用变量
const tablePaging = ref({
  // sortBy: '',
  // descending: false,
  page: 1,
  rowsPerPage: props.pageSize,
  rowsNumber: 0, // 仅服务端分页时定义该属性
});
// Table 加载中标志位
const isTableLoading = ref(false);
// Page loaded DONE
function onPageLoaded(args) {
  const { page, rowsPerPage } = args.pagination;
  isTableLoading.value = false;
  tablePaging.value.page = page;
  tablePaging.value.rowsPerPage = rowsPerPage;
  resetTableScrollTop();

  if (Object.hasOwn(args, 'totalCount')) {
    const total = args.totalCount;
    tablePaging.value.rowsNumber =
      total == null ? tablePaging.value.rowsNumber : total;
  }
}
// Table 触发的加载数据事件
function onRequest(args) {
  isTableLoading.value = true;
  $emit('page-load', {
    ...args,
    /**
     * @param {Object} params
     * @param {Number} params.totalCount
     */
    done: params => {
      onPageLoaded({ ...args, ...params });
    },
  });
}
// 表格翻页后重置滚动位置
function resetTableScrollTop() {
  if (!props.resetScrollOnPageTurning || !props.list.length) return;
  const scrollContainer = tableRef.value.$el.querySelector(
    '.q-table__grid-content',
  );
  if (!scrollContainer) return;
  scrollContainer.scrollTo({
    top: 0,
    behavior: 'smooth', // 使用平滑滚动
  });
}

//#endregion

/** #region TTable 且 pagingType 为 pagination 时，分页器增加简单模式 pagerStyle.simple */

const isSimplePager = computed(() => {
  return mappingProps.pagerStyle === PAGER_STYLES.simple;
});
const pageMax = computed(() => {
  const { rowsPerPage, rowsNumber } = tablePaging.value;
  return rowsPerPage ? Math.ceil(rowsNumber / rowsPerPage) : 0;
})

const simplePageCurrent = ref(1);
watch(simplePageCurrent, (v) => {
  tablePaging.value.page = v;
  onRequest({ pagination: tablePaging.value })
});
watch(() => mappingProps.pagerStyle, (v) => {
  if (v === PAGER_STYLES.simple) {
    simplePageCurrent.value = tablePaging.value.page || 1;
  }
});

/** #endregion pagerStyle.simple */

/**
 * 移动端下拉刷新时，禁止滚动穿透，阻止事件冒泡，
 * 以避免列表下拉刷新时，同时触发页面的下拉刷新
 * @param e
 * @remarks
 * - 2025.5.21 已替换下拉刷新组件，默认没有PC端下拉刷新
 */
const onTouchStart = (e) => {
  // e.stopPropagation();
};

onMounted(() => {
  if (props.pagingType === PAGING_TYPE.Pagination) {
    if (props.autoLoad) {
      onRequest({ pagination: tablePaging.value });
    }
  }
});
</script>

<!-- 组件整体宽高，由外部确定 -->
<template>
  <template v-if="props.pagingType === PAGING_TYPE.Pagination">
    <t-table
      ref="tableRef"
      grid
      :rows="list"
      v-model:pagination="tablePaging"
      :loading="isTableLoading"
      :hide-no-data="!showEmptyText"
      :no-data-label="emptyText"
      :rows-per-page-options="[5, 10, 15, 20, 25, 50, 100]"
      :card-container-style="{
        overflowY: 'auto',
        display: list.length ? 'block' : 'none',
      }"
      :style="{
        height: list.length ? computedTableHeight : null,
      }"
      @request="onRequest"
    >
      <template v-slot:item="slotProps">
        <slot name="item" v-bind="slotProps"></slot>
      </template>

      <template v-slot:loading>
        <t-inner-loading showing color="primary" />
      </template>

      <template v-if="isSimplePager" v-slot:bottom>
        <div class="pager-simple w-full flex flex-center">
          <t-pagination
            v-model="simplePageCurrent"
            :max="pageMax"
            icon-first='none'
            icon-last='none'
            input
            class="pager-simple__pagination"
          />
        </div>
      </template>
    </t-table>
  </template>
  <template v-else-if="mappingProps.isScrollOutside/*使用外部滚动*/">
    <scroll-outside
      ref="scrollOutsideRef"
      v-model:list="list"
      :page-size="mappingProps.pageSize"
      :auto-load="mappingProps.autoLoad"
      :preload-offset="mappingProps.offset"
      :empty-text="emptyText"
      @scroll-load="onOuterScrollLoad"
    >
      <template #default>
        <slot name="default"></slot>
      </template>
    </scroll-outside>
  </template>
  <template v-else>
    <t-list
      ref="listRef"
      v-show="mappingProps.visible"
      class="repeat-list scroll"
      :style="{
        height: computedHeight,
      }"
    >
      <!-- List 的默认插槽 -->
      <div
        v-if="showIndexAndCount"
        class="scroll-notification-container full-width row flex-center"
      >
        <div
          v-show="scrollCounting > 0 && listTotal > 0"
          class="scroll-notification"
          :class="[
            scrollCountingVisible
              ? 'scroll-notification__show'
              : 'scroll-notification__hide',
          ]"
        >
          {{
            _.template($q.lang.tRepeat.currentInTotal)({
              current: scrollCounting,
              totalCount: listTotal,
            })
          }}
        </div>
      </div>
      <v-pull-to-refresh
        v-if="pullRefresh"
        v-model="isRefreshing"
        :class="[!listTotal && isFinished ? 'fit' : '']"
        style="min-height: 48px;"
        :max-pull-distance="mappingProps.maxPullDistance"
        @refresh="onPullRefresh"
        @touchstart="onTouchStart"
      >
        <!-- PullToRefresh 的默认插槽 -->
        <t-infinite-scroll
          v-if="scrollContainerVisible"
          ref="scrollRef"
          @load="onScrollLoad"
          :offset="offset"
          class="fit"
        >
          <!-- InfiniteScroll 的 default 插槽 -->
          <template #default>
            <slot v-if="list && list.length" name="default"></slot>
            <div
              v-else-if="showEmptyText && isFinished"
              class="fit row justify-center"
            >
              <slot name="empty">
                <t-item class="q-my-md">
                  <NoData :text="emptyText"></NoData>
                </t-item>
              </slot>
            </div>
          </template>

          <!-- InfiniteScroll 的 loading 插槽 -->
          <template v-if="!refreshDone" #loading>
            <slot name="loading">
              <div class="row justify-center q-my-md">
                <t-spinner-dots color="primary" size="40px" />
              </div>
            </slot>
          </template>
        </t-infinite-scroll>
      </v-pull-to-refresh>
      <template v-else>
        <t-infinite-scroll
          v-if="scrollContainerVisible"
          ref="scrollRef"
          @load="onScrollLoad"
          :offset="offset"
          class="fit"
        >
          <!-- InfiniteScroll 的 default 插槽 -->
          <template #default>
            <slot v-if="list && list.length" name="default"></slot>
            <div
              v-else-if="showEmptyText && isFinished"
              class="fit row justify-center"
            >
              <slot name="empty">
                <t-item class="q-my-md">
                  <NoData :text="emptyText"></NoData>
                </t-item>
              </slot>
            </div>
          </template>

          <!-- InfiniteScroll 的 loading 插槽 -->
          <template v-if="!refreshDone" #loading>
            <slot name="loading">
              <div class="row justify-center q-my-md">
                <t-spinner-dots color="primary" size="40px" />
              </div>
            </slot>
          </template>
        </t-infinite-scroll>
      </template>
    </t-list>
  </template>
</template>

<style lang="scss">
.repeat-list {
  position: relative;
  ::v-deep(.q-pull-to-refresh__content) {
    height: 100%;
    width: 100%;
  }

  .scroll-notification-container {
    z-index: 1000;
    position: sticky;
    top: 0;

    .scroll-notification {
      padding: 4px 16px;
      border-radius: 14px;
      position: absolute;
      background-color: var(--t-gray-3);
      color: var(--t-gray-7);
      transition:
        top 0.3s,
        opacity 0.5s,
        visibility 0.8s;

      &.scroll-notification__show {
        opacity: 1;
        visibility: visible;
        top: 8px;
      }
      &.scroll-notification__hide {
        opacity: 0;
        visibility: hidden;
        top: -80px;
      }
    }
  }
}

.pager-simple {
  .pager-simple__pagination .q-pagination__content {
    & > button:first-child, & > button:last-child{
      display: none;
    }
    & > button {
      background: var(--t-gray-2);
      &:hover {
        background: var(--t-gray-4);
      }
    }
    .q-icon {
      color: var(--theme-color, --q-primary);
    }

    input.q-placeholder{
      &::placeholder {
        color: rgba(0,0,0,0.56) !important;
      }
    }
  }
}
</style>
